﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using System.IO;
using System.Xml;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace Fishes.Objects
{
    public class Fish
    {
        #region Enums

        public enum FishAIState
        {
            Idle,
            Swim,
            Dash,
            Dead
        }

        #endregion

        #region Properties

        public string Name
        {
            get { return _name; }
        }
        private string _name = String.Empty;

        public ObjectProperty Hunger
        {
            get { return _hunger; }
        }
        private ObjectProperty _hunger = new ObjectProperty();

        public int Speed
        {
            get { return _speed; }
        }
        private int _speed = 0;

        public Vector2 Size
        {
            get { return _size; }
        }
        private Vector2 _size = new Vector2();

        public Texture2D BackgroundImage
        {
            get { return _backgroundImage; }
        }
        private Texture2D _backgroundImage = null;

        public Vector2 Position
        {
            get { return _position; }
            set { _position = value; }
        }
        private Vector2 _position = new Vector2();

        public bool IsSelected
        {
            get { return _isSelected; }
            set { _isSelected = value; }
        }
        private bool _isSelected = false;

        #region AI

        public FishAIState AiState
        {
            get { return _aiState; }
            private set { _aiState = value; }
        }
        private FishAIState _aiState = FishAIState.Idle;

        private int AiSwim
        {
            get { return _aiSwim; }
            set { _aiSwim = value; }
        }
        private int _aiSwim = 0;

        private int AiDash
        {
            get { return _aiDash; }
            set { _aiDash = value; }
        }
        private int _aiDash = 0;

        private Vector2 MoveToDestination
        {
            get { return _moveToDestination; }
            set { _moveToDestination = value; }
        }
        private Vector2 _moveToDestination = Vector2.Zero;

        private bool IsFading
        {
            get { return _isFading; }
            set { _isFading = value; }
        }
        private bool _isFading = false;

        private TimeSpan FadeTime
        {
            get { return _fadeTime; }
            set { _fadeTime = value; }
        }
        private TimeSpan _fadeTime = TimeSpan.Zero;

        #endregion

        #endregion

        #region Public Methods

        public Fish()
        {
            this.Hunger.OnDecrement += new ObjectProperty.OnDecrementDelegate(Hunger_OnDecrement);
        }

        public bool Load(string path, GraphicsDevice g)
        {
            try
            {
                FileStream fp = File.Open(path, FileMode.Open);

                XmlDocument doc = new XmlDocument();
                doc.Load(fp);

                this._name = doc.SelectSingleNode("fish/name").InnerText;

                if (!this._hunger.Load(doc, "fish/hunger"))
                    throw new InvalidOperationException(String.Format("Failed to load ObjectProperty: fish/hunger - {0}",
                        path));

                this._speed = Convert.ToInt32(doc.SelectSingleNode("fish/speed").InnerText);

                int w = Convert.ToInt32(doc.SelectSingleNode("fish/width").InnerText);
                int h = Convert.ToInt32(doc.SelectSingleNode("fish/height").InnerText);
                this._size = new Vector2(w, h);

                if (this.BackgroundImage != null)
                {
                    this._backgroundImage.Dispose();
                    this._backgroundImage = null;
                }

                string backgroundFileName = doc.SelectSingleNode("fish/backgroundImage").InnerText;
                this._backgroundImage = Texture2D.FromFile(g, backgroundFileName, w, h);

                this._aiSwim = Convert.ToInt32(doc.SelectSingleNode("fish/aiSwim").InnerText);
                this._aiDash = Convert.ToInt32(doc.SelectSingleNode("fish/aiDash").InnerText);

                fp.Close();
                fp.Dispose();
            }
            catch (Exception ex)
            {
                return false;
            }

            return true;
        }

        public bool Destroy()
        {
            if (this.BackgroundImage != null)
            {
                this.BackgroundImage.Dispose();
                this._backgroundImage = null;
            }

            return true;
        }

        public void Update(GameTime gt)
        {
            if (this.AiState == FishAIState.Dead)
                this.IsSelected = false;

            if (this.AiState != FishAIState.Dead)
            {
                this.Hunger.Update(gt);
            }

            if (this.IsSelected)
            {
                this.AiState = FishAIState.Idle; // reset
                this.MoveToDestination = Vector2.Zero;

                MouseState ms = Mouse.GetState();
                float x = ms.X;
                float y = ms.Y;

                if (x < 0.0f)
                    x = 0.0f;
                if (x - this.Size.X > Game1.Tank.Size.X)
                    x = Game1.Tank.Size.X - this.Size.X;

                if (y < 0.0f)
                    y = 0.0f;
                if (y - this.Size.Y > Game1.Tank.Size.Y)
                    y = Game1.Tank.Size.Y - this.Size.Y;

                this._position.X = x;
                this._position.Y = y;
            }
            else
            {
                switch (this.AiState)
                {
                    case FishAIState.Idle:
                        {
                            int p = Game1.Random.Next(100); // get a percentage

                            bool computeMoveTo = false;

                            if (p < this.AiSwim)
                            {
                                this.AiState = FishAIState.Swim;
                                computeMoveTo = true;
                            }
                            else if (p < this.AiDash)
                            {
                                this.AiState = FishAIState.Dash;
                                computeMoveTo = true;
                            }

                            if (computeMoveTo)
                            {
                                int x = Game1.Random.Next((int)Game1.Tank.Size.X - (int)this.Size.X); // don't want to leave the tank
                                int y = Game1.Random.Next((int)Game1.Tank.Size.Y - (int)this.Size.Y);
                                this.MoveToDestination = new Vector2(x, y);
                            }

                            break;
                        }
                    case FishAIState.Swim:
                        {
                            if (this.MoveTo(gt, this.MoveToDestination, this.Speed))
                            {
                                this.AiState = FishAIState.Idle;
                            }

                            break;
                        }
                    case FishAIState.Dash:
                        {
                            if (this.MoveTo(gt, this.MoveToDestination, this.Speed * 2))
                            {
                                this.AiState = FishAIState.Idle;
                            }

                            break;
                        }
                    case FishAIState.Dead:
                        {
                            if (!this.IsFading && this.MoveTo(gt, new Vector2(this.Position.X, 0), 1))
                            {
                                this.IsFading = true;
                            }
                            else if (this.IsFading)
                            {
                                if (this.Fade(gt))
                                    Game1.Tank.RemoveFish(this);
                            }

                            break;
                        }
                    default:
                        throw new InvalidOperationException(String.Format("{0} is an unknown FishAIState",
                            this.AiState));
                }
            }
        }

        public void Render(SpriteBatch sb)
        {
            Rectangle dest = new Rectangle();
            dest.X = (int)this.Position.X;
            dest.Y = (int)this.Position.Y;
            dest.Width = (int)this.Size.X;
            dest.Height = (int)this.Size.Y;

            if (this.IsSelected)
                sb.Draw(this.BackgroundImage, dest, Color.Blue);
            else if (this.AiState == FishAIState.Dead)
            {
                if (this.IsFading)
                {
                    if (this.FadeTime.Milliseconds % 2 == 1)
                    {
                        Rectangle source = new Rectangle(0, 0, (int)this.Size.X, (int)this.Size.Y);
                        sb.Draw(this.BackgroundImage, dest, source, Color.Red, 0.0f, new Vector2(0, 0),
                            SpriteEffects.FlipHorizontally, 0);
                    }
                }
                else
                {
                    Rectangle source = new Rectangle(0, 0, (int)this.Size.X, (int)this.Size.Y);
                    sb.Draw(this.BackgroundImage, dest, source, Color.Red, 0.0f, new Vector2(0, 0),
                        SpriteEffects.FlipHorizontally, 0);
                }
            }
            else
                sb.Draw(this.BackgroundImage, dest, Color.White);
        }

        #endregion

        #region Private Methods

        private bool Fade(GameTime gt)
        {
            this._fadeTime = this._fadeTime.Add(gt.ElapsedGameTime);
            return this.FadeTime.Seconds > 5;
        }

        private bool MoveTo(GameTime gt, Vector2 vec, int speed)
        {
            Vector2 v = this.Position - vec;
            this._position.X -= (float)((v.X + speed) * gt.ElapsedGameTime.TotalSeconds);
            this._position.Y -= (float)((v.Y + speed) * gt.ElapsedGameTime.TotalSeconds);

            return (Math.Abs((int)(this.Position.X - vec.X)) <= speed * 2 &&
                Math.Abs((int)(this.Position.Y - vec.Y)) <= speed * 2);
        }

        private void Hunger_OnDecrement(ObjectProperty op)
        {
            if (op.CurrentValue <= 0)
                this.AiState = FishAIState.Dead;
        }

        #endregion
    }
}
